<?php
namespace SimpleMVC;

require __DIR__ . '/lib/Log.php';

use SimpleMVC\lib\Log;
use SimpleMVC\lib\Router;

class SimpleMVC
{

    private static $app;

    private $config = array();

    private $errors = array();

    private function __construct()
    {}

    public static function run()
    {
        self::$app = new SimpleMVC();
        self::$app->loadConfig();
        self::$app->init();
        self::$app->handleRequest();
    }

    public static function getConfig($key = null)
    {
        if ($key === null) {
            return self::$app->config;
        } else {
            return isset(self::$app->config[$key]) ? self::$app->config[$key] : null;
        }
    }

    private function loadConfig()
    {
        $configFile = APP_PATH . 'config' . DIRECTORY_SEPARATOR . 'Config.php';
        if (file_exists($configFile)) {
            $this->config = include $configFile;
            if (! is_array($this->config)) {
                $this->config = array();
            }
        }
    }

    private function init()
    {
        if (isset($this->config['log_save_path'])) {
            Log::setSavePath($this->config['log_save_path']);
        }
        $composerAutoLoadFile = VENDOR_PATH . 'autoload.php';
        if (file_exists($composerAutoLoadFile)) {
            include $composerAutoLoadFile;
        }
        spl_autoload_register(array(
            $this,
            'autoLoad'
        ));
        set_error_handler(array(
            $this,
            'errorHandler'
        ));
        set_exception_handler(array(
            $this,
            'exceptionHandler'
        ));
        register_shutdown_function(array(
            $this,
            'shutDown'
        ));
    }

    private function handleRequest()
    {
        $urlSuffix = isset($this->config['url_suffix']) ? $this->config['url_suffix'] : '';
        $urlRules = isset($this->config['url_rules']) && is_array($this->config['url_rules']) ? $this->config['url_rules'] : array();
        $router = new Router($urlSuffix, $urlRules);
        $routes = $router->getPossibleRoutes();

        foreach ($routes as $route) {
            if (! class_exists($route['class'])) {
                continue;
            }
            $controller = new $route['class']();
            if (method_exists($controller, $route['method'])) {
                $controller->{$route['method']}();
                return;
            }
        }

        $this->show404Page();
    }

    private function autoLoad($className)
    {
        $targetClassFile = null;
        if (strpos($className, __NAMESPACE__) === 0) {
            $classFile = str_replace(__NAMESPACE__ . '\\', '', $className);
            $classFile = str_replace('\\', DIRECTORY_SEPARATOR, $classFile);
            $targetClassFile = SYS_PATH . $classFile . '.php';
        } else {
            $classFile = str_replace('\\', DIRECTORY_SEPARATOR, $className);
            $targetClassFile = APP_PATH . $classFile . '.php';
        }
        if ($targetClassFile != null && file_exists($targetClassFile)) {
            include $targetClassFile;
        }
    }

    public function shutDown()
    {
        $error = error_get_last();
        if ($error != null) {
            $this->errors[] = array(
                'from' => 'error catched by shut down function',
                'code' => $error['type'],
                'message' => $error['message'],
                'file' => $error['file'],
                'line' => $error['line']
            );
            if ((E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR) & $error['type']) {
                while (ob_get_level() > 0) {
                    ob_end_clean();
                }
                http_response_code(500);
            }
        }
        if (count($this->errors) > 0) {
            Log::error($this->formatErrors());
        }
    }

    public function errorHandler($severity, $message, $file, $line)
    {
        $this->errors[] = array(
            'from' => 'error handler',
            'code' => $severity,
            'message' => $message,
            'file' => $file,
            'line' => $line
        );
    }

    public function exceptionHandler($e)
    {
        $this->errors[] = array(
            'from' => 'exception handler',
            'code' => $e->getCode(),
            'message' => $e->getMessage(),
            'file' => $e->getFile(),
            'line' => $e->getLine()
        );
        while (ob_get_level() > 0) {
            ob_end_clean();
        }
        http_response_code(500);
        exit(1);
    }

    private function show404Page()
    {
        http_response_code(404);
        include SYS_PATH . 'errorpages' . DIRECTORY_SEPARATOR . 'error_404.php';
        exit();
    }
    
    private function formatErrors()
    {
        $text = '';
        foreach ($this->errors as $error) {
            $text .= "Error [message: {$error['message']}, code: {$error['code']}, file: {$error['file']}, line: {$error['line']}, catched by: {$error['from']}]\r\n";
        }
        return $text;
    }
}